﻿/*
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * See product pages for more information.
 * http://closesudoku.codeplex.com
 * Copyright (C), 2010, hrondra
 */

using System;
using System.Collections.Generic;
using System.Text;

namespace Close.Classes
{
    /*
     * Simple undo/redo functionality
     */
    class RevertingChanges
    {
        /// <summary>
        /// Empty value
        /// </summary>
        /// <remarks>To recognize nothing in value/note</remarks>
        public const Int16 NothingInValue = -1981;

        /// <summary>
        /// Empty value - to have the value in positive.
        /// </summary>
        /// <remarks>To recognize nothing in value/note</remarks>
        private const Int16 NothingInValuePositive = -NothingInValue;

        #region "Singleton"

            /// <summary>
            /// Singleton - variable with history
            /// </summary>
            private static readonly RevertingChanges _history = new RevertingChanges();

            /// <summary>
            /// For a which game the history is
            /// </summary>
            private static uint _gameId = uint.MaxValue;

            /// <summary>
            /// Singleton - history for undo/redo actions.
            /// </summary>
            public static RevertingChanges UndoRedoHistory
            {
                get
                {
                    return _history;
                }
            }
        #endregion
        
        /// <summary>
        /// Object for storing history
        /// </summary>
        public class SudokuBoardAction
        {
            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="rowColumnPosition">Number with column and row</param>
            /// <param name="oldValue">value</param>
            public SudokuBoardAction(Byte rowColumnPosition, Int16 oldValue)
            {
                this.rowColumn = rowColumnPosition;
                this.stepValue = oldValue;
            }
            public Int16 stepValue;
            public Byte rowColumn;      //can be in the Int - this is easier
        }

        /// <summary>
        /// All commands
        /// </summary>
        private List<SudokuBoardAction> _historyList = new List<SudokuBoardAction>();

        /// <summary>
        /// Current index (changing by undo/redo)
        /// </summary>
        private int _lastStepIndex = -1;

        private int lastStepIndex 
        {
            get { return _lastStepIndex; }
            set { _lastStepIndex = value; stepChanged(); }
        }


        /// <summary>
        /// Can do undo
        /// </summary>
        public bool CanUndo
        {
            get
            {
                return lastStepIndex >= 0;
            }
        }

        /// <summary>
        /// Can do redo
        /// </summary>
        public bool CanRedo
        {
            get
            {
                return (lastStepIndex + 1 < _historyList.Count);
            }
        }

        /// <summary>
        /// Total number of steps in history.
        /// </summary>
        public int Count
        {
            get
            {
                return _historyList.Count;
            }
        }

        /// <summary>
        /// Add step value to the history list.
        /// </summary>
        /// <param name="newValueIsNote">new value is note</param>
        /// <param name="oldValue">previous value</param>
        /// <param name="newValue">next note - not used</param>
        public void Push(byte RowColumn, Int16 oldValue, Int16 newValue, bool newValueIsNote)
        {
            if (oldValue == 0 && !newValueIsNote)
                oldValue = NothingInValuePositive;

            if (lastStepIndex != _historyList.Count - 1)
                _historyList.RemoveRange(lastStepIndex + 1, _historyList.Count - lastStepIndex - 1);

            if (newValueIsNote)
                _historyList.Add(new SudokuBoardAction(RowColumn, oldValue));
            else
            {
                //negative values for "normal" numbers (not note)
                _historyList.Add(new SudokuBoardAction(RowColumn, (Int16)(-1 * oldValue))); ;
            }
            lastStepIndex++;
        }

        /// <summary>
        /// Add step value to the history list.
        /// </summary>
        public void Push(byte RowColumn, char oldValue, char newValue)
        {
            Push(RowColumn, convertCharToByte(oldValue), convertCharToByte(newValue), false);
        }

        
        /// <summary>
        /// Number in char to its byte presentation (0 for empty).
        /// </summary>
        public static byte convertCharToByte(char value)
        {
            if (value == ' ')   //TODO: helper class
                return 0;
            else
                return (byte)(value - '0');
        }

        /// <summary>
        /// Change index.
        /// </summary>
        private void Pop()
        {
            if (lastStepIndex >= 0)
                lastStepIndex--;
            else
            {
                MyLog.Write(MyLog.LogType.Error, "Undo", "Can not do undo: " + lastStepIndex.ToString() + this.ToString());
            }
        }

        /// <summary>
        /// If it is necessary, remove the history.
        /// </summary>
        /// <param name="gameId">game id</param>
        public void Clear(uint gameId)
        {
            if (gameId != _gameId)
            {
                _history.Clear();
                gameId = _gameId;
            }
        }
        /// <summary>
        /// Remove all the steps from the history.
        /// </summary>
        public void Clear()
        {
            lastStepIndex = -1;
            _historyList.Clear();
        }

        /// <summary>
        /// Show history.
        /// </summary>
        public override string ToString()
        {
            StringBuilder strB = new StringBuilder();
            foreach (SudokuBoardAction act in this._historyList)
                strB.Append(String.Format("{0}: {1} ,", (act.rowColumn + 11).ToString() ,act.stepValue.ToString())); //TODO 2.0 specific
            if (strB.Length > 0)
                strB.Remove(strB.Length - 1, 1);
            return strB.ToString();
        }

        public SudokuBoardAction Undo()
        {
            SudokuBoardAction r = _historyList[lastStepIndex];
            Pop();
            return r;
        }

        public SudokuBoardAction Redo()
        {
            if (lastStepIndex < _historyList.Count)
                lastStepIndex++;
            else
            {
                MyLog.Write(MyLog.LogType.Error, "Redo", "Can not do redo: " + lastStepIndex.ToString() + this.ToString());
            }
            return _historyList[lastStepIndex];
        }

        public void UndoAfter(byte RowColumn, Int16 oldValue, Int16 newValue)
        {
            _historyList[lastStepIndex + 1] = new SudokuBoardAction(RowColumn, oldValue);
        }

        public void RedoAfter(byte RowColumn, Int16 oldValue, Int16 newValue)
        {
            _historyList[lastStepIndex] = new SudokuBoardAction(RowColumn, oldValue);
        }

        public delegate void UndoChangeRedoDelagate();
        /// <summary>
        /// One second elapsed - return seconds from the begenning
        /// </summary>
        public event UndoChangeRedoDelagate StepChanged;
        /// <summary>
        /// One second elapsed.
        /// </summary>
        private void stepChanged()
        {
            if (StepChanged != null)
                StepChanged();
        } 
    }
}